/* eslint-disable import/no-webpack-loader-syntax */
import controller from '!!raw-loader!./controller/controller.java'
import entity from '!!raw-loader!./entity/entity.java'
import mapper from '!!raw-loader!./mapper/mapper.java'
import mapperXml from '!!raw-loader!./mapper/mapper.xml'
import service from '!!raw-loader!./service/service.java'
import impl from '!!raw-loader!./service/impl/impl.java'
import menu from '!!raw-loader!./menu.sql'
import api from '!!raw-loader!./frontend/api.js'
import form from '!!raw-loader!./frontend/form.vue'
import view from '!!raw-loader!./frontend/view.vue'
import { computed, onBeforeMount, ref, watch, watchEffect } from 'vue'
// https://handlebarsjs.com/guide/
import Handlebars from 'handlebars'
import { downloadCode, getColumns } from './api'
import { camelize, capitalize, copy, uncapitalize } from '@web-utils/core'
import { fetchList, fetchSelectDsList } from '@/api/gen/gen'
import { services } from '@/config'
import { Message } from 'element-ui'

const hideColumns = [
  'id',
  'unid',
  'create_time',
  'create_by',
  'create_id',
  'dept_id',
  'dept_full_name',
  'update_time',
  'update_id',
  'update_by',
  'tenant_id',
  'del_flag'
].map(value => camelize(value.toLowerCase(), '_'))

Handlebars.registerHelper('form', (dataType, maxLength, prop, label, form) => {
  if (form) {
    let component
    if (dataType === 'datetime') {
      component = `<FormDatePicker v-model="entity.${prop}" prop="${prop}" label="${label}" />`
    } else if (dataType === 'tinyint') {
      component = `<FormSelect v-model="entity.${prop}" prop="${prop}" label="${label}" />`
    } else if (maxLength > 500) {
      component = `<FormTextarea v-model="entity.${prop}" prop="${prop}" label="${label}" max-length="${maxLength}"/>`
    } else {
      component = `<FormInput v-model="entity.${prop}" prop="${prop}" label="${label}" />`
    }
    component = `
        ${component}`
    return new Handlebars.SafeString(component)
  }
  return ''
})

Handlebars.registerHelper('table', (dataType, maxLength, prop, label, table) => {
  if (table) {
    return new Handlebars.SafeString(`
      <TableColumn label="${label}" prop="${prop}" />`)
  }
  return ''
})

Handlebars.registerHelper('search', (dataType, maxLength, prop, label, search, isProp) => {
  if (search) {
    if (isProp) {
      return new Handlebars.SafeString(`          ${prop}: null,
`)
    } else {
      return new Handlebars.SafeString(`
      <FormInput v-model="searchForm.${prop}" label="${label}" prop="${prop}" />`)
    }
  }
  return ''
})

Handlebars.registerHelper('javaField', (dataType, prop, label, form, columnType, javaType) => {
  if (!hideColumns.includes(prop)) {
    let type
    if (dataType === 'tinyint') {
      type = 'Integer'
    } else if (dataType === 'bigint') {
      type = 'Long'
    } else if (dataType === 'datetime') {
      type = 'LocalDateTime'
    } else {
      type = 'String'
    }
    const field = `
     /**
      * ${label}
      */
     @ApiModelProperty(value="${label}")
     private ${type} ${prop};`
    return new Handlebars.SafeString(field)
  }
  return ''
})

Handlebars.registerHelper('template', val => new Handlebars.SafeString(`$\{${val}}`))

export default function useTemplate() {
  const tpl = {
    'Controller.java': controller,
    'Entity.java': entity,
    'Service.java': service,
    'ServiceImpl.java': impl,
    'Mapper.java': mapper,
    'Mapper.xml': mapperXml,
    'menu.sql': menu,
    'api.js': api,
    'Form.vue': form,
    'View.vue': view
  }

  const templateNames = Object.keys(tpl)
  const temp = {}

  templateNames.forEach(value => {
    temp[value] = { name: value, path: '', template: '' }
  })

  const templates = ref(temp)

  const datasourceList = ref([])
  const tables = ref([])

  const serviceNames = ref([])

  serviceNames.value = Object.keys(services).map(value => ({ label: value, value: `services.${value}` }))

  const model = ref({
    formName: '',
    moduleName: '',
    tableComment: '',
    dsName: '',
    tableName: '',
    frontendFilePrefix: '',
    entityName: '',
    flowName: '',
    docType: '',
    docTypeName: '',
    entityFullName: '',
    path: '',
    serviceName: '',
    pkgName: 'com.fuyao.cloud.',
    frontendDir: '',
    requestPath: '',
    columns: []
  })

  model.value.serviceName = serviceNames.value[0].value

  const javaPath = 'backend/src/main/java/'
  const pkgPath = computed(() => model.value.pkgName.replace(/\./g, '/'))
  const javaSrc = computed(() => `${javaPath}${pkgPath.value}/`)
  const javaRes = 'backend/src/main/resources/'

  const rawColumns = ref([])

  function mapColumn(value) {
    const prop = camelize(value.columnName.toLowerCase(), '_')
    return {
      columnName: value.columnName,
      columnType: value.columnType,
      dataType: value.dataType,
      maxLength: value.maxLength,
      prop,
      label: value.comments,
      search: false,
      form: !hideColumns.includes(prop) && value.columnName.toLowerCase().indexOf('_id') < 0,
      table: !hideColumns.includes(prop) && value.columnName.toLowerCase().indexOf('_id') < 0
    }
  }

  function copyLink(value) {
    copy(value, () => Message.success('复制成功'))
  }

  function genPath() {
    templateNames.forEach(value => {
      let path = value
      if (['Controller.java', 'Mapper.java', 'Service.java'].includes(value)) {
        path = `${uncapitalize(value.replace('.java', ''))}/${model.value.entityName}${value}`
        path = `${javaSrc.value}${path}`
      } else if (value === 'ServiceImpl.java') {
        path = `service/impl/${model.value.entityName}${value}`
        path = `${javaSrc.value}${path}`
      } else if (value === 'Entity.java') {
        path = `entity/${model.value.entityName}.java`
        path = `${javaSrc.value}${path}`
      } else if (value === 'Mapper.xml') {
        path = `mapper/${model.value.entityName}${value}`
        path = `${javaRes}${path}`
      } else if (value === 'menu.sql') {
        path = `backend/${value}`
      } else if (['api.js', 'Form.vue', 'View.vue'].includes(value)) {
        if (['Form.vue', 'View.vue'].includes(value)) {
          path = `${model.value.frontendFilePrefix}${value}`
          path = `frontend/${path}`
        } else {
          path = `frontend/${value}`
        }
      }
      templates.value[value].path = path
    })
  }

  function genCode() {
    templateNames.forEach(value => {
      const template = Handlebars.compile(tpl[value])
      templates.value[value].template = template(model.value)
    })
  }

  async function getDs() {
    const res = await fetchSelectDsList()
    datasourceList.value = res.data.data.map(value => ({ label: value.name, value: value.name }))
    model.value.dsName = datasourceList.value[0].label
  }

  async function fetchColumns() {
    if (model.value.dsName && model.value.tableName) {
      const res = await getColumns(model.value.dsName, model.value.tableName)
      rawColumns.value = res.data.data
    } else {
      rawColumns.value = []
    }

    model.value.columns = rawColumns.value.map(mapColumn)

    genCode()
  }

  async function getTables() {
    const res = await fetchList({
      current: 1,
      size: 1000,
      dsName: model.value.dsName
    })

    tables.value = res.data.data.records.map(value => ({ ...value, label: value.tableName, value: value.tableName }))

    model.value.tableName = tables.value[0].label
  }

  const templateList = computed(() => {
    return templateNames.map(value => {
      return templates.value[value]
    })
  })

  watch(
    () => model.value.tableName,
    newVal => {
      model.value.tableComment = tables.value.find(value => value.tableName === newVal)?.tableComment
      model.value.moduleName = model.value.tableComment
      model.value.flowName = `${model.value.tableComment}流程`
      model.value.docType = newVal?.toUpperCase()
      model.value.docTypeName = model.value.tableComment
      model.value.formName = model.value.tableComment
      model.value.frontendDir = newVal?.toLowerCase()?.replace(/_/g, '-')
      model.value.entityName = capitalize(camelize(newVal, '_'))
      model.value.entityFullName = `${model.value.pkgName}.entity.${model.value.entityName}`
      model.value.frontendFilePrefix = model.value.entityName
      model.value.requestPath = model.value.entityName?.toLowerCase()
    }
  )

  watch(
    () => model.value.dsName + model.value.tableName,
    async () => {
      await fetchColumns()
    }
  )

  watchEffect(() => {
    model.value.path = `/${model.value.frontendFilePrefix}Form/:id`
  })

  watch(
    () => model.value.dsName,
    async () => {
      model.value.tableName = null
      await getTables()
    }
  )

  watch(
    model,
    () => {
      genPath()
      genCode()
    },
    { deep: true, immediate: true }
  )

  onBeforeMount(async () => {
    await getDs()
  })

  async function download() {
    await downloadCode({ templates: templateNames.map(value => templates.value[value]) })
  }

  return {
    templates,
    datasourceList,
    tables,
    model,
    download,
    serviceNames,
    copyLink,
    templateList
  }
}
